package elms

import (
	"gopkg.in/mgo.v2/bson"
	"k8s.io/apimachinery/pkg/util/net"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"
)

var (
	PRIVILEGE_DEFAULT_ALL  = "all"
	PRIVILEGE_DEFAULT_NONE = "none"
	PRIVILEGE_DEFAULT_READ = "readOnly"
	PRIVILEGE_NONE         = 0
)

type User struct {
	Id       bson.ObjectId   `bson:"id,omitempty"`
	Name     string          `bson:"name,omitempty"`
	GroupIds []bson.ObjectId `bson:"groupIds,omitempty"`
}

type Token struct {
	Oid        *bson.ObjectId `bson:"oid,omitempty"`
	Uid        *bson.ObjectId `bson:"uid,omitempty"`
	Username   string         `bson:"username,omitempty"`
	RoleType   int            `bson:"roleType,omitempty"`
	Language   string         `bson:"language,omitempty"`
	ExpiresIn  int64          `bson:"expiresIn,omitempty"`
	CreateTime int64          `bson:"createTime,omitempty"`
	Token      string         `bson:"token,omitempty"`
	Privileges *Privileges    `bson:"privileges,omitempty"`
	Acls       []string       `bson:"acls,omitempty"`
}

type Privileges struct {
	Accept  []int  `json:"accept,omitempty"`
	Deny    []int  `json:"deny,omitempty"`
	Default string `json:"default,omitempty"`
}

func NewToken() *Token {
	return &Token{
		Language: "en",
	}
}

func (t *Token) Expired() bool {
	return t.CreateTime+t.ExpiresIn < time.Now().Unix()
}

func (t *Token) ForwardHeaders(r *http.Request) {
	query := r.URL.Query()
	r.Header.Set("X-Api-Username", t.Username)
	r.Header.Set("X-Api-Role-Type", strconv.Itoa(t.RoleType))
	r.Header.Set("X-Api-Language", t.Language)
	r.Header.Set("X-Api-Ip", net.GetClientIP(r).String())

	if t.Oid != nil {
		r.Header.Set("X-Api-Oid", t.Oid.Hex())
	}

	if t.Uid != nil {
		r.Header.Set("X-Api-Uid", t.Uid.Hex())
	}

	if t.Oid == nil || t.RoleType < 51 {
		// admin user or service
		sudo := query.Get("sudo")
		oidParam := query.Get("oid")

		if sudo == "true" || sudo == "1" {
			// client request to grant all resources in all orgs
			query.Del("oid")
		} else if bson.IsObjectIdHex(oidParam) {
			// admin pass oid in query params, do action in target org
			query.Set("oid", oidParam)
		} else {
			query.Set("oid", "0000000000000000000abcde")
		}
	} else {
		// normal user permitted to self owned org
		query.Set("oid", t.Oid.Hex())
	}

	if len(t.Acls) > 0 {
		r.Header.Set("X-Api-Acls", strings.Join(t.Acls, ","))
	}

	query.Set("access_token", t.Token)
	r.URL.RawQuery = query.Encode()
	r.RequestURI = r.URL.RequestURI()
}

func (p *Privileges) Authorize(id int) bool {
	if id == PRIVILEGE_NONE {
		return true
	}

	for _, deny := range p.Deny {
		if deny == id {
			return false
		}
	}

	for _, accept := range p.Accept {
		if accept == id {
			return true
		}
	}

	switch p.Default {
	case PRIVILEGE_DEFAULT_NONE:
		return false
	case PRIVILEGE_DEFAULT_ALL:
		return true
	case PRIVILEGE_DEFAULT_READ:
		return Odd(id)
	}

	return false
}

func Odd(n int) bool {
	return math.Mod(float64(n), 2.0) == 1.0
}
